package com.atguigu.spzx.manager.interceptor;

import com.atguigu.spzx.common.util.AuthUtils;
import com.atguigu.spzx.model.common.ResultCodeEnum;
import com.atguigu.spzx.model.entity.sys.SysUser;
import com.atguigu.spzx.model.exception.SpzxException;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.concurrent.ConcurrentHashMap;

@Component
public class LoginAuthInterceptor implements HandlerInterceptor {
//    public static final ConcurrentHashMap<Thread, SysUser> CACHE_MAP = new ConcurrentHashMap();
    public static final ThreadLocal<SysUser> THREAD_LOCAL = new ThreadLocal<>();
    @Resource
    RedisTemplate redisTemplate;
    //请求放行给Controller之前执行：检查、日志记录
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //拦截所有请求时 将预检请求也拦截了  预检不会携带token访问后端接口
        //应该放行预检请求
        String method = request.getMethod();
        if(StringUtils.equalsIgnoreCase("OPTIONS" , method)){
            return true;
        }
        //1、获取请求头token
        String token = request.getHeader("token");
        if(StringUtils.isEmpty(token)){
            throw new SpzxException(ResultCodeEnum.LOGIN_EXPIRE_ERROR , null);
        }
        //2、使用token到redis中获取登录状态
        Object sysUser = redisTemplate.opsForValue().get("sys:user:login:" + token);
        if(sysUser==null){
            throw new SpzxException(ResultCodeEnum.LOGIN_EXPIRE_ERROR , null);
        }
        //可以使用token作为key
//        Thread.currentThread() 正在执行当前方法的线程对象 每个线程栈空间私有
//        CACHE_MAP.put(Thread.currentThread() , (SysUser) sysUser);
        //获取当前线程对象的Map，使用THREAD_LOCAL作为键，传入的参数作为值
        //不存在并发   性能要高于ConcurrentHashMap缓存数据
        THREAD_LOCAL.set((SysUser) sysUser);

        AuthUtils.set((SysUser) sysUser);
        return true;
    }
    //controller返回结果后执行：返回结果处理、日志记录、收尾
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //内存泄露问题：代码中已经使用完的对象，仍然被某些变量持有引用 如果GC时，不能及时回收该对象
        //移除使用完的对象: 只考虑多线程共享的数据(静态属性、成员属性)
        //CACHE_MAP.remove(Thread.currentThread());
        THREAD_LOCAL.remove();

        AuthUtils.remove();
    }
}
